<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="../css/common.css" media="all" />
<link rel="stylesheet" type="text/css" href="../css/article.css" media="all" />
</head>
<body>
<div id="w3h_body">
  <div class="body_content">
    <!-- toc begin -->
    <h1 class="title">BX2047: 各浏览器对 onbeforeunload 事件的支持与触发条件实现有差异</h1>
  <ul class="toc">
      <li><a href="#standard_reference">标准参考</a> <span>•</span></li>
      <li><a href="#description">问题描述</a> <span>•</span></li>
      <li><a href="#influence">造成的影响</a> <span>•</span></li>
      <li><a href="#impacted_browsers">受影响的浏览器</a> <span>•</span></li>
      <li><a href="#analysis_of_issues">问题分析</a> <span>•</span></li>
      <li><a href="#solutions">解决方案</a> <span>•</span></li>
      <li><a href="#see_also">参见</a></li>
    </ul>
    <!-- toc end -->
    <div id="w3h_content">
      <!-- content begin -->
      <address class="author">作者：钱宝坤</address>
      <h2 id="standard_reference">标准参考</h2>
      <p>无。</p>

      <h2 id="description">问题描述</h2>
      <p>一般情况下，onbeforeunload 事件处理函数内会写入一些提示性语句，当用户的浏览器跳转到其他页面时，用来提醒用户当前页面将要跳转，请用户决定是否观看新页面。<br />
      或者在 onbeforeunload 事件内处理一些业务逻辑，在浏览器跳转到新页面之前 ，执行一些业务逻辑，如保存用户浏览信息等。</p>
      <p>简单的说这个事件应仅在页面 URL 发生变化时触发，但是在 IE 中 使用 JavaScript 伪协议执行脚本程序时，也会触发 onbeforeunload 事件。</p>
      
      <h2 id="influence">造成的影响</h2>
      <p>此问题不会造成大问题，但会导致不友好的提示出现，稍微影响用户操作体验。</p>

      <h2 id="impacted_browsers">受影响的浏览器</h2>
      <table class="list">
        <tr>
          <th>所有浏览器</th>
          <td>&nbsp;</td>
        </tr>
      </table>

      <h2 id="analysis_of_issues">问题分析</h2>
      <p>onbeforeunload 事件是非 W3C DOM-Event 标准事件，它属于 BOM (Browser Object Model) 范畴。到现在为止 BOM 还没有被标准化，它由各个浏览器厂商制定，因此会有实现差异。</p>
      <p>时至今日，HTML5 规范草案中已经开始标准化 BOM，遗憾的是 onbeforeunload 事件的触发条件还没有在草案中作出详细说明。</p>
      <p>更多内容可参考：<a href="http://www.w3.org/TR/html5/webappapis.html#handler-window-onbeforeunload">6.1.6.2 Event handlers on elements, Document objects, and Window objects</a>。</p>
      <p>最初的 onbeforeunload 事件支持是由 IE4.0 版本提供，存在于 BODY、FRAMESET 的 DOM 对象及 window 对象之中，随后被其他浏览器复制，但具体事件触发方式并没有统一。</p>
      <p>根据 MSDN 中描述，IE 的 onbeforeunload 事件可由以下这些条件触发：</p>
      <ul>
        <li>关闭当前浏览器窗口。</li>
        <li>导航到另一个进入一个新的地址或选择一个喜欢的位置。</li>
        <li>单击后退，前进，刷新，或主页按钮。</li>
        <li>点击一个链接到新页面。</li>
        <li>调用 超链接的 click 方法。</li>
        <li>调用 document.write 方法。</li>
        <li>调用 document.open 方法。</li>
        <li>调用 document.close 方法。</li>
        <li>调用 window.close 方法。</li>
        <li>调用 window.open 方法，窗口名称设置值为 _self。</li>
        <li>调用 window.navigate 或 NavigateAndFind 方法。</li>
        <li>调用 location.replace 方法。</li>
        <li>调用 location.reload 方法。</li>
        <li>指定一个 location.href 属性的新值。</li>
        <li>使用 submit 按键提交表单，或调用 form.submit 方法。 </li>
      </ul>
      <p>更详细的说明可以查考 MSDN 原文：<a href="http://msdn.microsoft.com/en-us/library/ms536907(VS.85).aspx">onbeforeunload Event</a>。</p>
      <p>在这些触发条件中绝大多数都使页面产生了跳转，但还缺少一些常见情况说明，即页面 URL 可能发生了变化但没有产生跳转。比如 &quot;javascipt:&quot; &quot;mailto:&quot; 等常见的浏览器内置伪协议，以及由第三方或用户自定义的为协议时，页面并不跳转，而是根据伪协议执行指定的行为。这个情况应加入到触发条件中。</p>
      <p>根据以上所有这些触发条件，我们构建如下代码来检测各浏览器对 onbeforeunload 事件的支持程度与触发条件：</p>
      <pre>&lt;script&gt;
window.onbeforeunload=function(){
  return &quot;请点击取消留在此页&quot;;
}
&lt;/script&gt;
请手工关闭当前浏览器窗口。&lt;br/&gt;
请手工单击后退，前进，刷新，或主页按钮。&lt;br/&gt;
请手工在地址栏输入其他页面地址或从收藏夹、历史记录中将页面导航其他站点。&lt;br/&gt;
&lt;a href=&quot;http://www.google.com&quot; id=&quot;A&quot;&gt;点击一个链接到新页面&lt;/a&gt;&lt;br /&gt;
&lt;button onclick=&quot;document.getElementById('A').click()&quot;&gt;调用 anchor.click 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;document.write('A')&quot;&gt;调用 document.write 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;document.open()&quot;&gt;调用 document.open 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;document.close()&quot;&gt;调用 document.close 方法。&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;window.open('http://www.google.com','_self')&quot;&gt;调用 window.open方法，窗口名称设置值为 _self。&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;try{window.navigate('http://www.google.com')}catch(e){alert('不支持此方法')}&quot;&gt;调用 window.navigate 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;try{window.external.NavigateAndFind('http://www.google.com','','')}catch(e){alert('不支持此方法')}&quot;&gt;调用 NavigateAndFind 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;location.replace('http://www.google.com')&quot;&gt;调用 location.replace 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;location.reload()&quot;&gt;调用 location.reload 方法&lt;/button&gt;&lt;br /&gt;
&lt;button onclick=&quot;location.href='http://www.google.com'&quot;&gt;指定一个 location.href 属性的新值&lt;/button&gt;&lt;br /&gt;
&lt;form action=&quot;http://www.google.com&quot; id=&quot;B&quot;&gt;
&lt;input type=&quot;submit&quot; value=&quot;提交具有action属性的一个表单&quot;&gt;
&lt;/form&gt;
&lt;button onclick=&quot;document.getElementById('B').submit()&quot;&gt;调用 form.submit 方法&lt;/button&gt;
&lt;a href=&quot;javascript:&quot;&gt;调用 javascipt: 伪协议&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;mailto:&quot;&gt;调用 mailto: 伪协议&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;custom:&quot;&gt;调用自定义伪协议&lt;/a&gt;
</pre>
      <p>执行结果汇总入表：</p>
      <table class="compare">
        <tr>
          <th>&nbsp;</th>
          <th>IE</th>
          <th>Firefox</th>
          <th> Chrome Safari </th>
          <th>Opera</th>
        </tr>
        <tr>
          <th style="text-align:left;">关闭当前浏览器窗口  </th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">导航到另一个进入一个新的地址或选择一个喜欢的位置</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">单击后退，前进，刷新，或主页按钮</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">点击一个链接到新页面</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 anchor.click方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_5">不支持此方法</span><sup>1</sup></td>
          <td><span class="hl_5">不支持此方法</span><sup>1</sup></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 document.write方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_2"> 事件未触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 document.open方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_2"> 事件未触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 document.close方法</th>
          <td><span class="hl_2"> 事件未触发</span></td>
          <td><span class="hl_2"> 事件未触发</span></td>
          <td><span class="hl_2"> 事件未触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 window.open方法，窗口名称设置值为 _self</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 window.navigate </th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_5">不支持此方法</span><sup>2</sup></td>
          <td><span class="hl_5">不支持此方法</span><sup>2</sup></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用  NavigateAndFind方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_5">不支持此方法</span><sup>3</sup></td>
          <td><span class="hl_5">不支持此方法</span><sup>3</sup></td>
          <td><span class="hl_5">不支持此方法</span><sup>3</sup></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 location.replace 方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 location.reload 方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr style="text-align:left;">
          <th style="text-align:left;">指定一个 location.href 属性的新值</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">使用 submit 按键提交表单</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 form.submit 方法</th>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_4"> 事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 javascipt: 伪协议</th>
          <td><span class="hl_4">事件被触发</span></td>
          <td><span class="hl_2">事件未触发</span></td>
          <td><span class="hl_2">事件未触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用 mailto: 伪协议</th>
          <td><span class="hl_2">事件未触发</span></td>
          <td><span class="hl_2">事件未触发</span></td>
          <td><span class="hl_4">事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
        <tr>
          <th style="text-align:left;">调用自定义伪协议</th>
          <td><span class="hl_4">事件被触发</span></td>
          <td><span class="hl_4">事件被触发</span></td>
          <td><span class="hl_4">事件被触发</span></td>
          <td><span class="hl_1">不支持该事件</span></td>
        </tr>
      </table>
      <p class="comment">注 1: 直接调用链接元素的 click 方法模拟鼠标点击事件，只有 IE 和 Opera 支持，<a href="BX9052">BX9052: IE Opera 支持使用 window.navigate 方法控制页面跳转</a> 和 <a href="SD9025">SD9025: IE6 IE7 IE8 Opera 支持除 INPUT 和 BUTTON 元素以外的其他元素的 'click' 方法</a>。
      <br />注 2: 使用 window.navigate 方法导航网页仅被 IE Opera 支持，可参考 MSDN 原文：<a href="http://msdn.microsoft.com/en-us/library/ms536638(v=VS.85).aspx">navigate Method</a>。
      <br />注 3: NavigateAndFind 方法处于 window.external 对象中，external 对象也仅 IE 支持，可参考 MSDN 原文：<a href="http://msdn.microsoft.com/en-us/library/ms536641(v=VS.85).aspx">NavigateAndFind Method</a> 和本站文章   <a href="BT9012">BT9012: IE 的 external 对象提供的方法是 IE 特有的</a>。</p>
      <p>结合汇总表可以看出：</p>
      <ul>
        <li>Opera 并不支持 onbeforeunload 事件。</li>
        <li>Chrome Safari 在调用 document.write、document.open、document.close 方法以及 &quot;javascipt:&quot; 伪协议时，不会触发 onbeforeunload 事件。</li>
        <li>Firefox 在调用 document.close 方法和 &quot;javascipt:&quot;、&quot;mailto:&quot; 伪协议时，不会触发 onbeforeunload 事件。</li>
        <li>IE 浏览器在调用 document.close 方法和 &quot;mailto:&quot; 伪协议时，不会触发 onbeforeunload 事件。</li>
      </ul>
      
      <h2 id="solutions">解决方案</h2>
      <p>onbeforeunload 事件还未标准化，各浏览器的支持以及事件触发条件差异较多，需谨慎使用。</p>
      <p>如必须在非 Opera 浏览器内使用该事件，应尽量避免在页面中调用常见的 &quot;javascrpt:&quot; 以及其他伪协议，以此回避不同浏览器中 onbeforeunload 事件被频繁触发。</p>
      
      <h2 id="see_also">参见</h2>
      <h3>知识库</h3>
      <ul class="see_also">
        <li><a href="#">...</a></li>
      </ul>
      <h3>相关问题</h3>
      <ul class="see_also">
        <li><a href="BT9012">BT9012: IE 的 external 对象提供的方法是 IE 特有的</a></li>
        <li><a href="BX2032">BX2032: 某些情况下浏览器不按照 "target" 属性所指目标打开协议链接</a></li>
        <li><a href="BX2003">BX2003: 特定的 URL 伪协议需安装提供该协议的特定软件才有效</a></li>
        <li><a href="BX9052">BX9052: IE Opera 支持使用 window.navigate 方法控制页面跳转</a></li>
      </ul>

      <div class="appendix">
        <h2>测试环境</h2>
        <table class="list">
          <tr>
            <th>操作系统版本:</th>
            <td>Windows 7 Ultimate build 7600</td>
          </tr>
          <tr>
            <th>浏览器版本:</th>
            <td>
              IE6<br />
              IE7<br />
              IE8<br />
              Firefox 3.6.10<br />
              Chrome 7.0.544.0 dev<br />
              Safari 5.0.2<br />
              Opera 10.62
            </td>
          </tr>
          <tr>
            <th>测试页面:</th>
            <td><a href="../../tests/BX2047/onbeforeunload_event_differences.html">onbeforeunload_event_differences.html</a></td>
          </tr>
          <tr>
            <th>本文更新时间:</th>
            <td>2010-10-13</td>
          </tr>
        </table>

        <h2>关键字</h2>  
        <!-- keywords begin -->
        <p>javascipt URL Protocol onbeforeunload </p>
        <!-- keywords end -->
      </div>
      <!-- content end -->
    </div>
  </div>
</div>
</body>
</html>
